移动端数据应用工厂
这是一种针对腾讯云公共云数据多场景、多维度看板的数据可视化 low code 配置的解决方案,是可配置化的前端 low code 项目。
背景
腾讯云可能有 100+ 公有云产品。他们有着不同的分类,比如:产品维度、部门维度、客户维度、渠道维度、行业维度、销售维度、... 而对于这些数据应用层需求各式各样,例如管理者们十分关注对于腾讯云上的收入、客户数据的实时分布,并且希望能够在小程序、移动端就可以方便查看;而销售们则只关注负责销售产品更下钻的数据产品...
另外,现有 pc 的可搭建系统存在比较多的问题:
- pc 仪表盘项目很老,虽然也提供了响应式的移动端页面,但是因为是老项目,组件不够丰富(满足不了新的功能需求),历史包袱重。汇报看板这种对时间要求高的需求,仪表盘的迭代速度跟不上。
- 权限颗粒度问题,老的仪表盘权限颗粒度只到了表级别,如果希望有更新的颗粒度,比如行权限,经过评估改造成本大, 也怕会影响其他现有的表。
- pc 仪表盘功能丰富,存在大量的其他模块的代码,汇报看板用它显得很重。更希望有轻量级的应用,来快速迭代,并往可搭建、low code 的方向去做,以此来节省开发的人力。
因此,继续一个权限管控更细,迭代更快,开发更灵活的方案来做这个事。
配置
1. ui 前端组件渲染
属性名:
- is
- props
- on
$attrs$listener- children
关键词:
- JSX
- 可视化搭建
- 组件传参
2. dataOptions 取数逻辑
属性名:
- WorksheetId 工作表 id
- Config 小计
- Order 排序
- Fields 字段筛选。 默认会拉出全部的字段,做字段筛选的。
- Filters 值筛选
- Variables 筛选和小计 ?
关键词:
- Id 映射到实际的表名
- Node 开发完成之后给到一份协议文档
前端
要点:
- 沉淀组件库
- markdown-demo-loader
- 如何提高组件、项目开发环境的编译速度。
关键词:
- jsx + vue + config
- 动态 component + is 标签渲染组件,传入 dataSource 渲染图块
- Module 层包裹 Component 层,拥有 loadData 的行为,在指定时机触发取数操作。另外根据 dataOptions 中的 handler 函数名,在取数回来之后,对数据做加工处理,供 Component 层做直接渲染。
- Module 也可以包裹多个 Component,用于多个相同组件共享一份渲染数据。
- Table, Line, List 组件所需的数据结构是不同的,需要做额外的处理。这部分的逻辑最开始是写在 Module 组件的 Methods 中,不过这部分与业务逻辑几乎无关,已经在慢慢处理成 npm 包的形式了,一是便于单元测试,而是便于很方便的移植到 node BFF 层去。
- Module 层设计了一些钩子函数:
- onShow 判断组件是否可见,如果不可见的话,就不会继续后面的钩子。
- beforeLoad 会根据 dataOptions 中的传参,在发请求之前处理一下请求参数 options
- loaded 已经触发的取数操作,一般会在这里做数据的规范化, 处理 rowData 成为 dataSource
- error 会将 error 传到上层组件中做统一处理。
- 如果通用的配置和数据函数还是不能满足,或者统一在 Module 出配置不够灵活的话,每一个 Component 其实可以自定义补充一些处理函数,或者是自定义事件。
- Modules 层会包裹多个 Module。Tabbars, Tabs 会包裹 Modules 组成一个个的看板。 同时这几个模块都是继承 Module 的,都拥有这些钩子和取数能力。
- 筛选条件一类的组件,没有取数逻辑,只会触发事件, 或者在组件设计的时候,我们会考虑受控和非受控。非受控简单理解为,没有传值和事件进去,也能够正常工作,而受控则需要额外传入属性和事件才可以工作。一般会考虑设计成受控的,在 vue 的 jsx 中,需要重新通配 vModel 的能力,也就是给大多数筛选组件都定义 model 自定义表单。来控制 Module/Modules 级别的 state.
开发流程
- 总的一个配置文件。 一份配置文件,对应一个 App
- auth 接口登录, 拉取 tabs, tabbars 的权限,从 App Config 中筛选拼接出当前角色的 Config
- 动态注册路由,addRoute。 其他的前端项目一般会考虑,全部路由都注册, 但是通过 router 的 meta 携带参数来判断页面是否有权限。 这样子,前后端都需要维护一份角色、权限的配置信息。 另外一个用户可能包含多个角色,每次做一次筛选也不方便。这样做至少只需要维护一份权限配置即可。
- atomConfig to chart
数据来源
- 计费平台的流水表, 例如 cos 100 元,200 元等
- 数仓的同学,处理 tdw, 写 Python 脚本开发 日表、月表、季表等
- 数仓同学最终会通过洛子任务,将数据出库到结果库中,这里就是数仓的主题层?结果库中包含很多其他的数据库,例如:
- mysql
- clickHouse
- PG
- ...
- 工程组这里会拿一些宽表,做一些拼接,比如补充说明之类的,或者多表关联,这是在线的工作表做的事情。当然,针对一份数据宽表,因为数据的隐私性,需要加入行权限,那这里的权限筛选也是在工作表(这个服务中)处理的。 所谓的权限筛选,其实在真正的结果数据中,是不存在的。
- 权限的定义和维护,需要依赖另一个平台,定义了角色和权限点的概念。维护了一位同事与角色之间的关联关系。
- 表结构的基本样子:
- ftime
- gid
- cid
- uin
- productionName
- companyName
- ...
- 对于同一张宽表,以客户维度筛选、或者以公司维度筛选,如果处理成多个接口,肯定是不划算的。这里只需要通过不同的传参,来小计某一列即可。就可以做到搜 cid, gid 级别。TODO: 小计???
- sum ?
- with rollup ?
Node 逻辑
取数粘合层。
- 拼接图块的数据筛选条件, 配置指定筛选、过滤出的字段信息。
- 对当前表数据做权限筛选, AuthFilters
- 将条件拼接之后,执行动态 SQL 获取数据结果;或者将条件转化为底层元数据取数(多种数据库类型的抹平层)所需的条件,最终获取数据。
拼接图筛选条件
- 权限点
- select xxx from yyy where
- Filters
- Config
- Variables
- Fields
- WorksheetId
优点
- 开发更快
- 开发流程更快,各部分人力沟通成本小,各方面工作量都减少了。
不足
- 开发环境下,可能就是这么直接用 Node 从结果库中取数据了。 一般如果宽表不是很大的话,速度慢的没有那么明显,但是如果没有优先处理好输出,直接这样拿出来的数据就会很大。
- 部分接口是实时 SQL,目前的缓存策略,较为暴力。
- 取数任务服务会经过 redis 缓存,定时任务 mock 角色来预加载缓存,但是因为页面越来越多,定时任务 mock 所有角色跑所有页面的数据也不是很科学,未命中 cache 的话, 速度就会很慢。优化措施如下:
- 底表接着分库分表。
- 开发环境下, 无法做到颗粒度很细的 render ??? 常常因为修改了一个图块的配置信息,而导致整个页面都重新加载了。
渲染加速
- 虚拟列表
- 数据懒加载
技术点
- vue
- 可视化
- echart
- 通过 json 配置文件生成目录结构
- 小程序 + webview
权限
权限颗粒度
- 行数据
- 表
- 项目(路由、模块)
- ?? 是啥来着?
权限管控
- 权限点
- 角色
权限链路
- mysql 配置库中,对应设置了一个字段属性,node 中将它读取出来之后,会根据里面的内容,做数据筛选。
角色与权限
前端对于角色的权限解决有两种方案:
- router 全量注册,通过 beforeEnter 钩子前端来判断当前角色是否有权限进入该页面
- 默认只注册不设权限的页面,通过 auth 接口拿到有权限的列表,通过 filter 全量的配置文件内容,addRoutes 动态注册有权限的路由。
后者比前者好的点:权限问题后台也需要拉取,前者需要两边都拉取,用映射关系各自做权限判断处理,一来两边都计算会不统一,修改权限需要改两处,二来权限 map 全量返回前端或者前端直接写死有泄漏风险。
后者缺点:需要额外做一次配置的筛选操作,返回给前端处理。
为什么要做一个移动端的数据应用?
- pc 仪表盘迭代速度跟不上,图表组件库不够丰富。
- pc 仪表盘的业务逻辑很重,系统优化历史包袱重,充满了不同环境、入口的兼容性代码,大量的 if else 判断环境。 针对汇报看板这个场景,希望有一个轻量、高性能的应用让 leader/GM 有更好的体验。
- 数据应用对数据的权限控制颗粒度更细,需要更加灵活的支持多种角色的权限管控,所以更适合站在巨人的肩膀上另起炉灶。
怎么做的?
TODO: 重点原理
- 前端: 基于 vue JSX + 自定义的 JSON Schema 规范。分为 ui 和 dataOptions 属性,分别对应 ui 和取数逻辑。
- 组件库的处理。 vant + echarts + table + 自定义的图表组件。
- Node: 基于 SQL 语法定义的自定义 JSON Schema 搜索语句。
- worksheetId 数据源
- Filters 筛选
- Configs 小计
- Variables 条件配置
步骤
- 首先定义了一个渲染的基类组件 comp, 注册了很多 components, 并传入 is 表示组件名,dataSource 用于渲染组件内容的数据。
- 定义最小模块 module,它拥有 beforeLoad, loaded, Error 几个钩子函数。 需要传入 ui 和 dataOptions 两个属性
- 多个 module 可以组成一个 modules, 就可以直接拼装成一个页面。默认情况下,modules 不会自动发出请求,因为不会给它传 dataOptions,但是实际上 modules 是从 module, 它也拥有单独取数的能力。
- 组件的几种分类:
- 数据渲染组件,例如 table, line,条形图、柱状图等,在封装了一层 echarts 的情况下, 这些组件都是直接在 comp 组件中注册的,意味着只要给 comp 传 is 和 dataSource 就可以正常渲染。
- 筛选的功能组件,这种组件 change 之后会改变取数的条件,用于更新数据。 这里就需要考虑如何与事件消费中心打通,
renderChild
一个主 Vue 实例渲染所有组件的形式。
问题:
- 最大的问题之一, bff 层的对接,不是直连后台服务。解决方式: 手动填入数据,或者接口
- 组件生态不够好,设计师设计稿很好看,前期组件沉淀还不够,不能够迅速搭建起移动端页面。
- 传统的可视化搭建系统存在的最大问题是,基本上都是单个页面的,比如活动页,表单收集页等,因为路由切换、多页数据共享等逻辑会与传统可搭建系统的纯粹性产生冲突,当然也不排除一些业务本身的场景比较简单。从编译的角度上来做的 搭建系统就可以解决这个问题,所谓的搭建,其实就是在写 dom json 。
与其他业界的 low code 项目的区别在哪里?
成果
经过该数据应用工厂底座能力创建的数据应用已经有 5 款,上线速度快,效果体验好。
搭建系统的优点和不足
亮点:
- mock 平台,做了数据权限管控和模拟用户的功能,能够让定时任务很好每天定时跑,一来预加载缓存,二来作为一个接口的自动化测试
- 节省了很多的人力
不足:
- 实时 SQL,加入的缓存是对参数 md5 之后的缓存,直接而强硬。
优点:
- 开发时间更快,数仓、后台、前端三个方向的开发工作量都更小了。
维度
- 产品
- 客户
- ...
表命名
- 收入明细表
- 毛利表
- top 损益
- top 收入客户等
难点
项目最大的难点
以前的难点基本都已经过去了。现在难? 产品迭代快,换了几波人,没有按照最开始设计的样子进行提
- 多个卡片公用一个数据;一个页面里有公用的时间筛选器,也有卡片自己维护的。
- 取数接口太纯粹,没有任何的输数据处理逻辑。这就导致没法简单地在前端直接拖拽,需要先填数据源,筛选条件,数据处理函数(产品、运营是不知道有什么函数可以调用的。。。)
- 同事计划新开一个服务,去做数据转化
- 这样可以也可以,但是我觉得这里还是用中间件的形式去处理比较好。
同比 环比
一级行业提取一个分类筛选。时间打横,二级行业作为维度,收入值是指标。
每一行增加一行该一级行业的小计。
分成了两个接口,请求。因为接口取数逻辑太纯粹,没法额外请求一次。
- low code 设计,解决的问题,收获,还存在的问题 ???
项目难点
- 问项目难点引入到重构, 在项目中做了什么事情有成就感,也引入到重构老项目。 成果: 开发速度的提升,工程、流程化规范了之后,减少发布、回滚的心智负担。
- 接手老的前端项目,接入监控系统,懒加载等性能优化。
最大的难点
我的工作中最大难点是,有时候会遇到技术边界以外的问题,我有两个解决办法,一是快速学习,二是快速搜索别人的现成方案
举个例子,我们蒜厂主站重度使用了 CodeMirror 这个轮子(我在它上面封装了七八个模块),然而这个轮子的文档写得并不好,很多时候我们会遇到一些需求,而这些需求文档里又写得非常模糊,这就比较头疼了。所以很多时候,我就要去不断地从文档的字里行间猜测,并结合源码一步一步地去跟踪,去尝试解决这个问题。但有时候确实超出了我的能力范围,那么我就会把我的问题提炼成一个小 demo,到 StackOverflow 去问,或者问一些同样用这个轮子的作者,甚至干脆去 issue 里问作者
(前端)难点
主要一块还是在数据通信。 因为换了几波产品经理、几波设计,会有多种场景的数据交互:
- 一个页面多个图表,公用同一个数据源,同一个筛选条件,也可能不同的筛选条件
- 多个页面共享一个筛选条件
- 同个页面,有多个触发重新取数的操作。
- 针对上面的几种公用数据的场景,存在:
- vuex 传统的使用方式会不够领会
- vue bus 在一定场景下面,会更灵活好用
- 数据的权限控制,沉淀了另一个平台:数据资产平台。
- 每个月固定跑数,一开始需要人工打开配置开关。
- 配置中心配置。 开发得到产品的指令, 手动配置。 开关开启、关闭,开发会对线上环境负责。
- 工作表配置。 project 颗粒度的权限,交给产品自己配置。产品权限太大, 不会关注、测试所有的模块,这是一个隐患。
- 上面都是人工手动进行修改。 这边是希望能够跟企业微信机器人打通,通过 api 的形式去处理。
数据通信
主要的难点是在于数据通信:
- 一个页面多个图表,用同一个数据源(需要减少请求)
- 多个页面共享一个筛选条件
- 同一个页面有多个触发条件取数的操作
- 每个月固定日期出数:
- 到时间,人工打开配置开关
- 定时任务修改配置文件,通过企业微信机器人对话即可自动修改配置(最主要的目的是控制出数的人不需要上配置平台,减少操作心智)
数据通信的解决方案:
- 直接写 vuex 肯定不够灵活。考虑动态加载模块 ?
- vue bus 自定义事件的形式更好用。
取数事件触发
- 日、周、月切换;时间选择
- 维度切换
等条件切换之后都有可能会重新触发取数。 但是如果每一个事件触发都直接发一个请求的话,会有几个问题:
- 首先肯定是浪费资源
- 其次因为筛选的条件不一致,如果前一个请求没有及时退出,可能会因为底层数据的多少差别,而导致图表数据闪烁(多个请求返回)
解决:
- 封装 axios 的时候,需要根据取数 ID 来存储 axios 的 token, 如果多个请求发出,需要再下一个请求发出之前先调用 token.cancel 函数退出请求
- 设计一个简洁的事件消费中心对象,每一个取数模块需要预先注册取数的触发事件名,条件组件触发之后,消费中心接收到事件比对触发事件名,若匹配则发出请求。
权限处理
- 每一张仪表盘、报表、工作表、推送等都会有一个管理员 or 负责人的概念,对表这个级别做控制。
- 行业经分、产品分析等会内嵌一些仪表盘,这些仪表盘是不做权限管控的,所有人都需要拉出数据。这部分的操作是通过,在配置库中指定某张工作表从指定 channel 来的时候,会走 no-check.
- 行数据的权限管控,在磐石上将所有部门名、产品名都做了录入,创建角色与这些权限点进行绑定。在工作表、仪表盘(底层还是工作表取数)的时候,还是动态拼接上了行权限(枚举出所有的产品名做一个拼接),最终返回数据。
- 产品权限与前端路由权限。 磐石统一控制,前端拿到权限点之后,动态 addRoutes 添加路由。
3. 重构
背景
hc 少,有很多稍微老一点的项目,以及很多前端项目都是外包处理的。
解决方案:
- js 改 ts
- 加入测试用例
- 加入 ci cd
- 升级 vue react 的依赖
- 抽离大量的公共 npm 包
- 引入微前端的概念
- 考虑 iframe 的重构
- 目的是为了实现页面的复用
- 从组件、脚手架级别实现复用
- 组件库
- 脚手架
- 种子项目
- json 生成页面和模块的方案, 直接生成代码的方案
其他重构或者重新搭建一个项目会考虑的点:
- 技术栈的确定。
- 目录结构的约定 router/services/store/components 等, 全都通过 webpack 自动加载。
- 工具库 npm 的引入,包含 axios 拦截封装,功能 utils 函数等
- 监控系统的接入, 满足当前的业务埋点需求 TAM + Sentry
- CI/CD Git 分支与环境的对应, 全部通过提交 MR 合并之后才会发布代码,自动跑单测、自动构建发布对应的环境,正式 Master 发布之后自动打 tag 提交 changelog 至 Master 分支。
- Husky + Eslint 校验代码规范、commit message 规范。 单元测试通过之后才给提交。
- 与后台开发同学确定好接口之后,书写 mock json 到一个 git 项目中,node mock server 会根据 json 的内容提供 mock 接口内容,便于前端先进行开发
重构 vue 的项目
- 目录结构规范
- 统一 @ 路径
- 统一 eslint 与 prettier
- 接入 ci/cd
- 某些依赖升级,比如 vue webpack 等
- 单元测试
自动生成 mock 的服务
https://jsonplaceholder.typicode.com/
物料:
- json demo
- js 对象
- class 类
- d.ts 文件
- pb 文件
通过词法分析,根据值或者类型来判断该 mock 的值的类型。通过数据资产平台,拉取数据,自动生成 mock 元素的结构。
namespace 与类、维度、指标的维护和共享。
- MOCK
- proxy: https://jsonplaceholder.typicode.com/ https://segmentfault.com/a/1190000008635891
- json-server
- 数据 mock 平台 https://github.com/thx/rap2-delos
基础服务
- 企业微信机器人
- 邮件
- html 转 inline
项目中遇到过哪些问题
项目内部问题
- 项目关键资源不足
- 关键项目遇到瓶颈
- 项目内部管理混乱
- 项目核心骨干流失
跨项目协作问题
- 跨项目成员动力不足
- 项目相关方沟通不足
产品工作问题
- 需求方杂乱、需求密集
- 紧急需求打乱日常节奏
- 日常重复性工作耗时较多
遇到最困难的事情,项目中最难的问题
是这样的,具体什么困难是最让我头疼的我已经记不清了,因为遇到困难这件事本身就是客观存在的,只要一个人还在前进,就会不断遇到困难。我在学习上、生活上不断解决各种问题的过程中得出了一些经验,这些经验让我成长了很多。比如,遇到困难时一定要保持心态良好,不要气馁和沮丧。如果要做一件事情频繁遇到困难,或者遇到的困难看起来很棘手,经验告诉我,那通常是路走错了,或者还没有找到正确的解决方式。这个时候保持心态良好,不气馁就很重要了。比如:之前导师让我写一个架构的 demo,但是那个架构的技术栈很多我都没有了解过,我开始是按照任务清单挨个往下执行,但是这个过程频繁遇到很棘手的问题,于是,我觉得是我的执行方法有问题,在一个下午我花了几个小时的时间,重新整理了一下思路,我把任务分解了一下,把每个我不熟悉的模块单独拉出来,写一些和其他模块几乎完全不相干的 demo,等到所有模块都熟悉的差不多了,然后再整合起来写一个完整的流程,这样一来,这件事情就被我解决了。生活中也是一样,遇到棘手的问题,保持心态良好,冷静的去思考更好的解决方式即可。
这个问题主要考察:1、能不能清晰认知自己的错误-认知能力;2、面对挫折你做了什么-解决问题的能力;3、学习到什么,怎样避免再次出现-自我完善能力;4、顺便通过讲述观察语言有没有逻辑性。
真实
传统前端开发到数据前端开发的转型,成长的过程我认为是一个让人痛苦又让人兴奋的过程。来数平的前半年,差不多都在做数据应用的事情, 从行业经分系统到 low code 可视化配置的事情,基本上我作为一个前端开发需要关注的事情,但是不了解整个业务逻辑。虽然做的事,是很完整的前端,从 node 到 h5 到小程序出口。
数据资产平台的开发,让我了解到整个数据链路,数据上报到 tdbank,等等中间的流程。
作用和价值
- 减少人力开发。复用取数和逻辑的原子能力?
Json 这种形式跟直接写代码相比,好在哪里
为什么不让产品、运营配,解放开发的人力
技术的规划升级
不足之处:
- 底层数据源来自大宽表,实际还是存在首次加载不够快的情况。
- 缓存机制是,请求的筛选参数值的 md5 值。意味着缓存跟某一个人是没有关系的,跟一个角色有关系。这样的话,只能暴力的清除某一个角色的缓存。
- 权限颗粒度分层明显,但不够灵活,直接取数的时候是可以绕过权限的逻辑,拿出原始数据。
- 有新的看板需求,前端同学压力大,新表接口需要写 node 配置,接着写前端的配置, 接着进行测试、预发、正式。
- 前端逻辑层, 很重。node 层取数逻辑很轻。 这样的设计,其实是沿用了,罗盘的取数逻辑。但是其实会存在不太合理之处,比如就工作量来说,如果前端和 node 同学一起加入,node 同学工作量会少很多。
解决方案:
- 数仓同学可以给到一定的支持,将权限部分的筛选结果直接跑到不同的细表里面去,node 做一个映射,保证数据的隐私性。
- 为了缓存,带权限接口与非权限接口有可能需要剥离??? 权限与缓存还需要再思考一下。
- 将公共的逻辑处理部分,比如将原始数据处理成 table 柱状图之类的纯函数逻辑,提取成 npm 包,慢慢一步步切到 serverless 中去。
- 业务方前端同学、或者内部开源的同学, 需要将文档、底层能力的接口、说明文档准备好,严格把控接口、组件的破坏性升级等。做好客服工作 ?
项目的问题
json 中无法写函数,写的函数无法很好的注入 content 或者 this 之类的上下文,除非改造函数的写法,比如 vm => vm.data 之类的将 this 传入 解决: 参考 VS Code 中的 ui 可配置化,采用 when 的形式,对操作符进行处理,将 when 字符串拼接上 with(this) 执行的接口会被自动 return, 这样就可以直接写属性名的判断条件,而不用带上 this 之类的前缀
随着项目的更新,旧的配置解析器慢慢显示出一些问题(项目初期设计不算太完善),但是线上已经有多个项目在跑了,新功能要加入旧解析器不能再很好的工作了。 解决: 优雅降级,对于老的组件,额外开发一些 transfor 转换器,将新配置(可读性更好)的配置转化成旧配置进行渲染; 对于新组件,直接将新的配置文件进行解析渲染。
可视化组件库的规范???
但是目前是手写 json 配置。
项目介绍
- 平台整个工作链路
- 介绍高管驾驶舱
low code 项目的价值在哪里? 提升了多少效率